import copy

from django.db import models
from AutoOperation.settings import STATICFILES_DIRS, AutoOperationExecutorManager
from tools.executorTools import Executor
from tools.requestTools import RequestThread
import threading
from tools.loggingConfig import logger
from tools.tool import joinAndMakePath
from conversion.imageToCover.PILMethod import makeImageCover
from conversion.imagesToVideo.cv2Method import imagesToVideo
from conversion.mergeAudioVideo.moviepyMethod import mergeAudioVideo
from conversion.textToAudio.baidu import baiduAudioApi
from tools.taskTools import Task
from tools.tool import dbImageBase64ListToBytesList, getAudioDuration
import os

# 视频尺寸
VIDEO_SIZE = (1280, 720)

# 视频中每张图片播放时长(s)
IMAGE_DURATION = 3


class BaseModel(models.Model):
    """
    数据库model基类
    """
    # 新闻标题
    title = models.CharField(max_length=255, null=False)
    # 新闻内容
    content = models.TextField(null=False)
    # 素材图片
    imagesBase64 = models.TextField(null=False)
    # 视频封面
    imageCoverPath = models.TextField(null=True)
    # 成品视频地址
    videoPath = models.TextField(null=True)
    # 素材日期
    date = models.DateTimeField(null=False)
    # 使用过该素材的Apps
    usedApps = models.TextField(null=False, default=[])

    class Meta:
        abstract = True


class CollectorExecutor(Executor, RequestThread):
    """
    数据采集
    执行器基类
    """

    def __init__(self, maxThreadSize, databaseModel):
        super(CollectorExecutor, self).__init__(maxThreadSize=maxThreadSize)
        # 存储数据的数据库model
        self.model = databaseModel
        # model锁
        self.modelLock = threading.Lock()

    def saveDB(self, **kwargs):
        """
        存储至数据库
        """
        with self.modelLock:
            self.model.objects.get_or_create(**kwargs)
        logger.info(f"{self.executorName} 存储了 {kwargs['title']}")


class ProducerExecutor(Executor):
    """
    成品制作
    执行器基类
    """

    def __init__(self, maxThreadSize, databaseModel):
        super(ProducerExecutor, self).__init__(maxThreadSize=maxThreadSize)
        # 存储数据的数据库model
        self.model = databaseModel
        # model锁
        self.modelLock = threading.Lock()

        # 创建成品存储路径
        self.imageCoversPath = joinAndMakePath(STATICFILES_DIRS[0], "imageCovers", f"{self.executorName}")
        self.audiosPath = joinAndMakePath(STATICFILES_DIRS[0], "audios", f"{self.executorName}")
        self.imagesVideosPath = joinAndMakePath(STATICFILES_DIRS[0], "imagesVideos", f"{self.executorName}")
        self.videosPath = joinAndMakePath(STATICFILES_DIRS[0], "videos", f"{self.executorName}")

    def executorEvent(self):
        try:
            needProducerDatas = self.model.objects.filter(videoPath__isnull=True)
            for needProducerData in needProducerDatas:
                task = Task(fun=self.producerEvent, taskName=needProducerData.title)
                self.putAndRunTask(task=task, needProducerData=needProducerData)
        except Exception as e:
            logger.error(f"{self.executorName} 成品制作出错 {e}")

    def producerEvent(self, needProducerData):
        try:
            pk = needProducerData.pk
            title = needProducerData.title
            imagesBytesList = dbImageBase64ListToBytesList(needProducerData.imagesBase64)
            content = needProducerData.content

            imageCoverPath = os.path.join(self.imageCoversPath, f"{pk}.jpg")
            audioPath = os.path.join(self.audiosPath, f"{pk}.mp3")
            imagesVideoPath = os.path.join(self.imagesVideosPath, f"{pk}.mp4")
            videoPath = os.path.join(self.videosPath, f"{pk}.mp4")

            # 制作封面
            makeImageCover(image_bytes=imagesBytesList[0], title=title, image_size=VIDEO_SIZE, savePath=imageCoverPath)

            # 文字转语音
            text = content
            if "原标题：" in text:
                text = "".join(text.split("原标题：")[1:])
            if "日报道" in text:
                text = "".join(text.split("日报道")[1:])
            baiduAudioApi(text=text, savePath=audioPath)

            # 制作视频
            audio_duration = getAudioDuration(filePath=audioPath)
            with open(imageCoverPath, "rb") as f:
                imageCoverBytes = f.read()
            imagesToVideo(video_duration=audio_duration, image_duration=IMAGE_DURATION, imageCoverBytes=imageCoverBytes,
                          image_bytes_list=imagesBytesList, video_size=VIDEO_SIZE, save_path=imagesVideoPath)

            # 视频语音合成
            mergeAudioVideo(audioPath=audioPath, imagesVideoPath=imagesVideoPath, savePath=videoPath)

            # 更新数据库
            imageCoverPath = imageCoverPath.split(STATICFILES_DIRS[0].__str__())[-1]
            videoPath = videoPath.split(STATICFILES_DIRS[0].__str__())[-1]
            self.updateDB(filterByTitle=title, imageCoverPath=imageCoverPath, videoPath=videoPath)

        except Exception as e:
            logger.error(f"{self.executorName} 成品制作出错 {e}")

    def updateDB(self, filterByTitle, **kwargs):
        """
        存储至数据库
        """
        with self.modelLock:
            self.model.objects.filter(title=filterByTitle).update(**kwargs)
        logger.info(f"{self.executorName} 成品制作已完成 【{filterByTitle}】")


class PublisherExecutor(Executor):
    """
    发布器
    执行器基类
    """

    def __init__(self, maxThreadSize):
        super(PublisherExecutor, self).__init__(maxThreadSize=maxThreadSize)

        """
        用于发布的数据库models
        """
        self.models = []
        # model锁
        self.modelLock = threading.Lock()

        """
        用于selenium自动化的Chrome远程接口
        """
        self.seleniumChromeDomain = "http://localhost:1234/wd/hub"

    def executorEvent(self):
        try:
            for model in copy.deepcopy(self.models):
                needPublisherDatas = model.objects.filter(imageCoverPath__isnull=False,
                                                          videoPath__isnull=False).exclude(
                    usedApps__contains=self.__class__.__name__)
                for needPublisherData in needPublisherDatas:
                    if model not in self.models:
                        break
                    task = Task(fun=self.publisher, taskName=needPublisherData.title)
                    self.putAndRunTask(task=task, model=model, needPublisherData=needPublisherData)
        except Exception as e:
            logger.error(f"{self.executorName} 成品发布出错 {e}")

    def publisher(self, model, needPublisherData):
        """
        发布器
        """
        # 执行发布流程
        self.publisherEvent(model=model, needPublisherData=needPublisherData)

        # 更新数据库
        title = needPublisherData.title
        usedApps = eval(needPublisherData.usedApps)
        usedApps.append(self.__class__.__name__)
        self.updateDB(model=model, filterByTitle=title, usedApps=usedApps)

    def publisherEvent(self, model, needPublisherData):
        """
        app发布逻辑
        用于子类重写业务逻辑
        @return:
        """
        pass

    def updateDB(self, model, filterByTitle, **kwargs):
        """
        存储至数据库
        """
        with self.modelLock:
            model.objects.filter(title=filterByTitle).update(**kwargs)
        logger.info(f"{self.executorName} 成品制作已完成 【{filterByTitle}】")
